﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;

namespace XOS_VHD_FAT32
{
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1, Size = 32)]
    struct DirEntry
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)]
        private char[] _Name;
        public string Name
        {
            get { return new string(_Name).ToUpper(); }
            internal set
            {
                var str = (value ?? "").PadRight(11, ' ');

                if (str.Length > 11)
                {
                    str = str.Substring(0, 11);
                }

                _Name = str.ToUpper().ToCharArray();
            }
        }
        [MarshalAs(UnmanagedType.U1)]
        public DirEntryAttrib Attrib;
        private byte Reserved;
        private byte CreationTimeLo;
        private UInt16 CreationTimeHi;
        private UInt16 CreationDate;
        private UInt16 AccessDate;
        private UInt16 FirstClusterHi;
        private UInt16 LastModTime;
        private UInt16 LastModDate;
        private UInt16 FirstClusterLo;
        public UInt32 Size;

        internal byte lfn_checksum()
        {
            return lfn_checksum(ASCIIEncoding.ASCII.GetBytes(_Name));
        }

        internal static byte lfn_checksum(string name)
        {
            if (name.Length > 11)
            {
                throw new ArgumentOutOfRangeException();
            }

            if (name.Length < 11)
            {
                var ext = Path.GetExtension(name);

                if (ext.Length > 0)
                {
                    ext = ext.Substring(1);
                }

                name = (Path.GetFileNameWithoutExtension(name).PadRight(8) + ext.PadLeft(3)).ToUpper();
            }

            return lfn_checksum(ASCIIEncoding.ASCII.GetBytes(name));
        }

        private static byte lfn_checksum(IEnumerable<byte> name)
        {

            byte sum = 0;

            foreach (byte c in name)
            {
                sum = (byte)(((byte)(sum & 1) << 7) + (byte)(sum >> 1) + c);
            }

            return sum;
        }

        internal static DirEntry Create(BootSect bootSect, uint clustId)
        {
            return new DirEntry
            {
                Name = "/",
                Attrib = DirEntryAttrib.Directory | DirEntryAttrib.System | DirEntryAttrib.Archive,
                Size = (uint)(bootSect.FATSize32 * bootSect.BytesPerSector),
                FirstClusterLo = (ushort)clustId,
                FirstClusterHi = (ushort)(clustId >> 16)
            };
        }

        public uint FirstCluster
        {
            get { return ((((uint)FirstClusterHi) << 16) | (uint)FirstClusterLo); }
            set
            {
                if (value < 2)
                {
                    throw new InvalidOperationException();
                }

                FirstClusterLo = (ushort)(value & 0xffff);
                FirstClusterHi = (ushort)((value >> 16) & 0xffff);
            }
        }

        public override string ToString()
        {
            return Name;
        }
    }
}
